# -*- coding: utf8 -*-
#
# Copyright (c) 2009 Matías Ribecky <status-notifications@mribecky.com.ar>
#
# This file is part of status-notifications.
# 
# status-notifications is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# status-notifications is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License along
# with status-notifications.  If not, see <http://www.gnu.org/licenses/>.

import json
import urllib2
import datetime
import webbrowser

import oauth.oauth as oauth
from pluginlib.plugin import Implementation

from statusnotifications.status import Status, User
from statusnotifications.config import Account
from statusnotifications.config import Account

__all__ = ['TwitterNetworkError', 'TwitterServiceError',
           'TwitterStatusProvider']

VERIFY_ACCOUNT_URL = 'http://api.twitter.com/1/account/verify_credentials.json'
FRIENDS_TIMELINE_URL = 'http://api.twitter.com/1/statuses/home_timeline.json'

REQUEST_TOKEN_URL = 'http://twitter.com/oauth/request_token'
ACCESS_TOKEN_URL = 'http://twitter.com/oauth/access_token'
AUTHORIZATION_URL = 'http://twitter.com/oauth/authorize'
# CALLBACK_URL = 'http://printer.example.com/request_token_ready'
VERIFY_URL = 'http://api.twitter.com/1/account/verify_credentials.json'

CONSUMER_KEY = 'ZCJBFSC9TWPC4R577OGQ'
CONSUMER_SECRET = 'ejz2QhhU9xWEfAV9Fg9Ulz7Wuakbnl9Z13oRlFY64'

def load(ctx):
    ctx.add_implementation(TwitterStatusProvider)
    ctx.add_implementation(TwitterNetworkError)
    ctx.add_implementation(TwitterServiceError)

class TwitterNetworkError(Implementation, Exception):
    iface_name = 'NetworkError'
    display_name = "Twitter network error"

    def __init__(self, *args, **kwargs):
        Implementation.__init__(self)
        Exception.__init__(self, *args, **kwargs)

class TwitterServiceError(Implementation, Exception):

    iface_name = 'StatusProviderError'
    display_name = "Twitter service error"

    def __init__(self, *args, **kwargs):
        Implementation.__init__(self)
        Exception.__init__(self, *args, **kwargs)

class TwitterStatusProvider(Implementation):
    '''Exposes the twitter api.

    .. attribute:: user
        :type: User

        The user with wich the requests authenticate.

    '''

    iface_name = 'StatusProvider'
    display_name = "Twitter user timeline"

    image_name = 'twitter_t_logo.png'

    def __init__(self, oauth_key, oauth_secret):
        self.user = None
        self._opener = None
        self.__last_id = None

        self._login(oauth_key, oauth_secret)

    def _login(self, oauth_key, oauth_secret):
        '''Creates the url opener with the authentication set up.'''

        self._consumer = oauth.OAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET)
        self._signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
        self._token = oauth.OAuthToken(oauth_key, oauth_secret)

        user_data = self._call_api(VERIFY_ACCOUNT_URL)
        self.user = User(user_data)

    def _call_api(self, url, **data):
        '''Request an url from the server and parse the output.

        :throws: NetworkError when connection to the server couldn't be
                    established.
        :trows: ApiError when the server returns an error.

        '''

        oauth_request = oauth.OAuthRequest.from_consumer_and_token(
                            self._consumer, token=self._token,
                            http_method='GET', http_url=url,
                            parameters=data)
        oauth_request.sign_request(self._signature_method,
                                   self._consumer,
                                   self._token)
        url = oauth_request.to_url()
        try:
            data_file = urllib2.urlopen(url)
            data = json.load(data_file)
            assert 'error' not in data # not sure if this happens, report if
                                       # it does.
            return data
        except urllib2.HTTPError, error:
            error_returned = error.read()
            error_data = json.load(error_returned)
            LOGGER.debug("Error calling '%s', returned '%s'", url,
                         error_returned)
            raise TwitterServiceError((error_data['request'],
                                       error_data['error']))
        except urllib2.URLError:
            raise TwitterNetworkError("Could not connect to the server")


    def get_new_statuses(self):
        '''Get a list of the friends' statuses.'''

        if self.__last_id is not None:
            data = {'since_id': self.__last_id}
        else:
            data = {}

        statuses = self._call_api(FRIENDS_TIMELINE_URL, **data)

#         skip_firstime = self.__last_id is None
        skip_firstime = False
        if statuses:
            self.__last_id = statuses[0]['id']

        for status in statuses:
            if not skip_firstime:
                status['created_at'] = parse_date(status['created_at'])
                status['user']['created_at'] = parse_date(
                                                  status['user']['created_at'])
                status['service_name'] = 'Twitter'
                yield Status(status)

    @classmethod
    def load_create_form(cls, form):

        user_data = {'pin': u''}

        user_data['consumer'] = oauth.OAuthConsumer(CONSUMER_KEY,
                                                    CONSUMER_SECRET)
        oauth_request = oauth.OAuthRequest.from_consumer_and_token(
                                                    user_data['consumer'],
                                                    http_url=REQUEST_TOKEN_URL)
        oauth_request.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(),
                                   user_data['consumer'],
                                   None)
        req = urllib2.Request(oauth_request.to_url())
        u = urllib2.urlopen(req)
        user_data['token'] = oauth.OAuthToken.from_string(u.read())

        oauth_request = oauth.OAuthRequest.from_token_and_callback(
                                                    token=user_data['token'],
                                                    http_url=AUTHORIZATION_URL)
        def open_browser():
            webbrowser.open(oauth_request.to_url())

        form.button_action(u"Allow access to your account:",
                           u"Open Browser",
                           open_browser)
        form.text_value(u"PIN:", user_data, 'pin')

        form.show_all()

        return user_data

    @classmethod
    def create_account(cls, user_data):
        oauth_request = oauth.OAuthRequest.from_consumer_and_token(
                                                     user_data['consumer'],
                                                     token=user_data['token'],
                                                     verifier=user_data['pin'],
                                                     http_url=ACCESS_TOKEN_URL)
        oauth_request.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(),
                                   user_data['consumer'],
                                   user_data['token'])
        req = urllib2.Request(oauth_request.to_url())
        u = urllib2.urlopen(req)
        token = oauth.OAuthToken.from_string(u.read())

        account = Account('Twitter-' + token.key,
                          cls,
                          token.key)
        account.password = token.secret
        return account

_MONTHS = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
          'Oct', 'Nov', 'Dec']

def parse_date(date_string):
    try:
        day_name, month_name, day, time, offset, year = date_string.split()
    except ValueError:
        day_name, month_name, day, time, year = date_string.split()

    time_format = '%H:%M:%S'
    time = datetime.datetime.strptime(time, time_format)
    date = time.replace(year=int(year),
                        month=_MONTHS.index(month_name),
                        day=int(day)
                        )
    return date

